<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Babylon.js 设备标签优化演示</title>
  <!-- 引入Babylon.js库 -->
  <script src="https://cdn.babylonjs.com/babylon.js"></script>
  <script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>
  <style>
    body { margin: 0; overflow: hidden; }
    canvas { width: 100%; height: 100%; }
    #info {
      position: absolute;
      top: 10px;
      left: 10px;
      background-color: rgba(0, 0, 0, 0.7);
      color: white;
      padding: 10px;
      font-family: Arial, sans-serif;
      font-size: 14px;
      z-index: 100;
    }
  </style>
</head>
<body>
  <canvas id="renderCanvas"></canvas>
  <div id="info">
    <p>设备数量: <span id="deviceCount">0</span></p>
    <p>可见标签: <span id="visibleLabels">0</span></p>
    <p>提示: 鼠标拖动旋转，滚轮缩放，Control+拖动平移</p>
  </div>

  <script>
    // 设备标签优化器类
    class DeviceLabelOptimizer {
      constructor(scene, options = {}) {
        this.scene = scene;
        this.options = {
          fontSize: options.fontSize || 24,
          fontFamily: options.fontFamily || 'Arial',
          maxVisibleLabels: options.maxVisibleLabels || 50,
          updateInterval: options.updateInterval || 1000,
          ...options
        };
        
        this.labels = new Map();
        this.visibleLabels = new Set();
        this.labelMeshes = new Map();
        this.labelPool = [];
        this.camera = scene.activeCamera;
        
        this._createSharedResources();
        this._setupUpdateLoop();
      }
      
      _createSharedResources() {
        // 创建共享材质
        this.sharedMaterial = new BABYLON.StandardMaterial("labelMaterial", this.scene);
        this.sharedMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        this.sharedMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        this.sharedMaterial.backFaceCulling = false;
        
        // 创建共享渲染目标纹理
        this.renderTarget = new BABYLON.RenderTargetTexture(
          "labelRenderTarget", 
          { width: 1024, height: 1024 }, 
          this.scene, 
          false, 
          true, 
          BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT, 
          false
        );
        
        // 创建共享平面网格
        this.sharedPlane = BABYLON.Mesh.CreatePlane("labelPlane", 1, this.scene);
        this.sharedPlane.isVisible = false;
        this.sharedPlane.isPickable = false;
      }
      
      _setupUpdateLoop() {
        setInterval(() => {
          this._updateVisibleLabels();
          document.getElementById('visibleLabels').textContent = this.visibleLabels.size;
        }, this.options.updateInterval);
        
        this.scene.onBeforeRenderObservable.add(() => {
          this._updateLabelPositions();
        });
      }
      
      addLabel(deviceId, deviceMesh, text) {
        const label = {
          deviceId,
          deviceMesh,
          text,
          mesh: null,
          isVisible: false
        };
        
        this.labels.set(deviceId, label);
        return label;
      }
      
      removeLabel(deviceId) {
        const label = this.labels.get(deviceId);
        if (label) {
          if (label.mesh) {
            this._releaseLabelMesh(label.mesh);
          }
          this.labels.delete(deviceId);
          this.visibleLabels.delete(deviceId);
        }
      }
      
      _acquireLabelMesh() {
        if (this.labelPool.length > 0) {
          return this.labelPool.pop();
        }
        
        const mesh = this.sharedPlane.clone(`labelMesh_${Date.now()}`);
        mesh.material = this.sharedMaterial.clone(`labelMat_${Date.now()}`);
        mesh.isPickable = false;
        return mesh;
      }
      
      _releaseLabelMesh(mesh) {
        mesh.isVisible = false;
        this.labelPool.push(mesh);
      }
      
      _updateVisibleLabels() {
        // 计算每个设备与相机的距离
        const deviceDistances = Array.from(this.labels.entries())
          .map(([deviceId, label]) => {
            const distance = BABYLON.Vector3.Distance(
              label.deviceMesh.getAbsolutePosition(),
              this.camera.position
            );
            return { deviceId, distance };
          })
          .sort((a, b) => a.distance - b.distance);
        
        // 确定可见的设备
        const newVisibleLabels = new Set();
        const maxVisible = Math.min(this.options.maxVisibleLabels, this.labels.size);
        
        for (let i = 0; i < maxVisible; i++) {
          newVisibleLabels.add(deviceDistances[i].deviceId);
        }
        
        // 更新可见性
        this.labels.forEach((label, deviceId) => {
          const shouldBeVisible = newVisibleLabels.has(deviceId);
          
          if (shouldBeVisible && !label.isVisible) {
            this._showLabel(label);
          } else if (!shouldBeVisible && label.isVisible) {
            this._hideLabel(label);
          }
        });
        
        this.visibleLabels = newVisibleLabels;
      }
      
      _updateLabelPositions() {
        this.visibleLabels.forEach(deviceId => {
          const label = this.labels.get(deviceId);
          if (label && label.mesh) {
            const position = label.deviceMesh.getAbsolutePosition().clone();
            position.y += 2; // 放在设备上方
            label.mesh.position = position;
            
            // 使标签始终面向相机
            label.mesh.lookAt(this.camera.position);
          }
        });
      }
      
      _showLabel(label) {
        if (!label.mesh) {
          label.mesh = this._acquireLabelMesh();
          
          // 创建动态纹理
          const texture = new BABYLON.DynamicTexture(
            `labelTexture_${label.deviceId}`,
            { width: 512, height: 128 },
            this.scene,
            false
          );
          
          label.mesh.material.diffuseTexture = texture;
          // 关键修改：翻转U坐标，解决文字显示问题
          label.mesh.material.diffuseTexture.invertU = true;
          label.mesh.material.diffuseTexture.invertV = true;
          
          label.mesh.scaling = new BABYLON.Vector3(2, 1, 1);
        }
        
        // 更新标签文本
        this._updateLabelText(label);
        
        label.mesh.isVisible = true;
        label.isVisible = true;
      }
      
      _hideLabel(label) {
        if (label.mesh) {
          label.mesh.isVisible = false;
        }
        label.isVisible = false;
      }
      
      _updateLabelText(label) {
        const texture = label.mesh.material.diffuseTexture;
        const context = texture.getContext();
        
        // 清空画布
        context.fillStyle = "rgba(0, 0, 0, 0.7)";
        context.fillRect(0, 0, texture.getSize().width, texture.getSize().height);
        
        // 设置文本样式
        context.fillStyle = "white";
        context.font = `${this.options.fontSize}px ${this.options.fontFamily}`;
        context.textAlign = "center";
        context.textBaseline = "middle";
        
        // 绘制文本
        context.fillText(label.text, texture.getSize().width / 2, texture.getSize().height / 2);
        
        // 更新纹理
        texture.update();
      }
      
      dispose() {
        this.labels.forEach(label => {
          if (label.mesh) {
            label.mesh.dispose();
          }
        });
        
        this.sharedMaterial.dispose();
        this.renderTarget.dispose();
        this.sharedPlane.dispose();
        
        this.labels.clear();
        this.visibleLabels.clear();
        this.labelMeshes.clear();
        this.labelPool = [];
      }
    }

    // 创建场景
    const canvas = document.getElementById('renderCanvas');
    const engine = new BABYLON.Engine(canvas, true);
    
    function createScene() {
      const scene = new BABYLON.Scene(engine);
      
      // 创建相机
            // 创建相机（替换原有 ArcRotateCamera）
      const camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(0, 100, 0), scene);
      camera.setTarget(BABYLON.Vector3.Zero()); // 设置相机目标点
      camera.attachControl(canvas, true);

      // 禁用相机旋转（只允许平移）
      camera.rotationEnabled = false;
      camera.checkCollisions = true; // 开启碰撞检测，防止穿墙
      // camera.applyGravity = true;    // 应用重力，保持在地面上

      // 设置移动速度
      camera.speed = 0.5;

      // 配置按键控制（可选）
      camera.keysUp = [69];      // E 键
      camera.keysDown = [81];    // Q 键
      camera.keysUpward = [87];      // W 键
      camera.keysDownward = [83];    // S 键
      camera.keysLeft = [65];    // A 键
      camera.keysRight = [68];   // D 键
      
      
      // const camera = new BABYLON.FollowCamera("FollowCam", new BABYLON.Vector3(0, 10, -10), scene);
      
      // const camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
      
      // const camera = new BABYLON.ArcRotateCamera(
      //   "camera", 
      //   -Math.PI / 2, 
      //   Math.PI / 3, 
      //   20, 
      //   new BABYLON.Vector3(0, 0, 0), 
      //   scene
      // );
      // camera.attachControl(canvas, true);
      // camera.lowerRadiusLimit = 5;
      // camera.upperRadiusLimit = 100;
      
      // 创建光源
      const light = new BABYLON.HemisphericLight(
        "light", 
        new BABYLON.Vector3(0, 1, 0), 
        scene
      );
      light.intensity = 0.7;
      
      // 创建地面
      const ground = BABYLON.Mesh.CreateGround("ground", 50, 50, 2, scene);
      const groundMaterial = new BABYLON.StandardMaterial("groundMaterial", scene);
      groundMaterial.diffuseColor = new BABYLON.Color3(0.1, 0.1, 0.1);
      ground.material = groundMaterial;
      
      // 随机生成设备
      const devices = [];
      const deviceCount = 100;
      
      for (let i = 0; i < deviceCount; i++) {
        // 随机位置
        const x = (Math.random() - 0.5) * 40;
        const z = (Math.random() - 0.5) * 40;
        const y = 0;
        
        // 随机大小
        const size = 0.5 + Math.random() * 1.5;
        
        // 随机类型（球体或立方体）
        let device;
        if (Math.random() > 0.5) {
          device = BABYLON.Mesh.CreateSphere(`device${i}`, 16, size, scene);
        } else {
          device = BABYLON.Mesh.CreateBox(`device${i}`, size, scene);
        }
        
        device.position = new BABYLON.Vector3(x, y, z);
        
        // 随机颜色
        const material = new BABYLON.StandardMaterial(`mat${i}`, scene);
        material.diffuseColor = new BABYLON.Color3(
          Math.random(), 
          Math.random(), 
          Math.random()
        );
        device.material = material;
        
        devices.push({
          id: i,
          mesh: device
        });
      }
      
      // 更新设备计数
      document.getElementById('deviceCount').textContent = deviceCount;
      
      // 设置标签优化器
      const labelOptimizer = new DeviceLabelOptimizer(scene, {
        fontSize: 28,
        maxVisibleLabels: 50
      });
      
      // 为每个设备添加标签
      devices.forEach(device => {
        labelOptimizer.addLabel(device.id, device.mesh, `设备#${device.id}`);
      });
      
      return { scene, labelOptimizer };
    }
    
    // 创建场景和标签优化器
    const { scene, labelOptimizer } = createScene();
    
    // 渲染循环
    engine.runRenderLoop(() => {
      scene.render();
    });
    
    // 响应窗口大小变化
    window.addEventListener('resize', () => {
      engine.resize();
    });
  </script>
</body>
</html>
